// TypeScript 基础类型
// Boolean 类型
let isDone:boolean = false;
// Number类型
let count: number = 10;
// String
let nameS: string = "Semliker";
// Array 类型
let list:number[] = [1,2,3]
let list1:Array<number> = [1,2,3]
// Enum 类型
// 数字枚举
enum Direction {
    NORTH = 1,
    SOUTH,
    EAST,
    WEST,
  }
let dir: Direction = Direction.NORTH;
// 字符串枚举
enum DirectionS {
    NORTH = "NORTH",
    SOUTH = "SOUTH",
    EAST = "EAST",
    WEST = "WEST",
}
// 异构枚举
// 异构枚举的成员值是数字和字符串的混合：
enum Enum3 {
    A,
    B,
    C = "C",
    D = "D",
    E = 8,
    F,
}
// Any 类型
// Unknown 类型
// unknown 类型只能被赋值给 any 类型和 unknown 类型本身。直观地说，这是有道理的：只有能够保存任意类型值的容器才能保存 unknown 类型的值。毕竟我们不知道变量 value 中存储了什么类型的值。
// 元组 Tuple 类型
// 数组一般由同种类型的值组成，但有时我们需要在单个变量中存储不同类型的值，这时候我们就可以使用元组。
let tupleType: [string, boolean];
tupleType = ["Semlinker", true];  //类型和数量都要对应
// 使用元组时，必须提供每个属性的值
// Void 类型
function warnUser():void{
    console.log("This is my warning message");    
}
// Null 和 Undefined 类型
// 默认情况下 null 和 undefined 是所有类型的子类型。 就是说你可以把 null 和 undefined 赋值给 number 类型的变量。然而，如果你指定了--strictNullChecks 标记，null 和 undefined 只能赋值给 void 和它们各自的类型。
// Never 类型
// never 类型表示的是那些永不存在的值的类型。 例如，never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型。
function infiniteLoop(): never {
    while (true) {}
}
// 在 TypeScript 中，可以利用 never 类型的特性来实现全面性检查，
type Foo = string | number;
function controlFlowAnalysisWithNever(foo: Foo) {
  if (typeof foo === "string") {
    // 这里 foo 被收窄为 string 类型
  } else if (typeof foo === "number") {
    // 这里 foo 被收窄为 number 类型
  } else {
    // foo 在这里是 never
    // 如果走到了这里会编译错误
    const check: never = foo;
  }
}
// 使用 never 避免出现新增了联合类型没有对应的实现，目的就是写出类型绝对安全的代码。

// TypeScript 断言
    // “尖括号” 语法
    let someValue:any =  "this is a string";
    let strLength:number = (<string>someValue).length;
    // as 语法
    let str1Length: number = (someValue as string).length;
// 类型守卫
// 类型保护是可执行运行时检查的一种表达式，用于确保该类型在一定的范围内
    // in关键字
    interface Admin{
        name:string,
        privileges:string[]
    }
    interface Employee {
        name: string;
        startDate: Date;
    }
    type UnknownEmployee = Employee | Admin;
    function printEmployeeInformation(emp: UnknownEmployee) {
        console.log("Name: " + emp.name);
        if ("privileges" in emp) {
          console.log("Privileges: " + emp.privileges);
        }
        if ("startDate" in emp) {
          console.log("Start Date: " + emp.startDate);
        }
    }
    // typeof关键字
    // typeof 类型保护只支持两种形式：typeof v === "typename" 和 typeof v !== typename，"typename" 必须是 "number"， "string"， "boolean" 或 "symbol"
    function padLeft(value: string, padding: string | number) {
        if (typeof padding === "number") {
            return Array(padding + 1).join(" ") + value;
        }
        if (typeof padding === "string") {
            return padding + value;
        }
        throw new Error(`Expected string or number, got '${padding}'.`);
      }
    //   instanceof 关键字
    interface Padder{
        getPaddingString():string;
    }
    class SpaceRepeatingPadder implements Padder {
        constructor(private numSpaces: number) {}
        getPaddingString() {
          return Array(this.numSpaces + 1).join(" ");
        }
      }
    class StringPadder implements Padder {
        constructor(private value: string) {}
        getPaddingString() {
            return this.value;
        }
    }
    let padder: Padder = new SpaceRepeatingPadder(6);
    if (padder instanceof SpaceRepeatingPadder) {
        // padder的类型收窄为 'SpaceRepeatingPadder'
    }
    // 自定义类型保护的类型谓词
    function isNumber(x: any): x is number {
        return typeof x === "number";
      }
    function isString(x: any): x is string {
        return typeof x === "string";
    }
// 联合类型和类型别名
    // 联合类型通常与 null 或 undefined 一起使用：
    const sayHello = (name: string | undefined) => {
        /* ... */
    };
// 可辨识联合
    // 它包含 3 个要点：可辨识、联合类型和类型守卫。
    enum CarTransmission {
        Automatic = 200,
        Manual = 300
    }
    // 声明了三个类型 他们都有一个共同的属性 vType
    interface Motorcycle {
        vType: "motorcycle"; // discriminant
        make: number; // year
    }
    interface Car {
        vType: "car"; // discriminant
        transmission: CarTransmission
    }
    interface Truck {
        vType: "truck"; // discriminant
        capacity: number; // in tons
    }
    // 声明一个联合类型 可能是三个中间的一个
    type Vehicle = Motorcycle | Car | Truck;
    // 使用switch case做类型守卫从而确保安全的访问到内部的属性
    const EVALUATION_FACTOR = Math.PI; 
    function evaluatePrice(vehicle: Vehicle) {
        switch(vehicle.vType) {
            case "car":
            return vehicle.transmission * EVALUATION_FACTOR;
            case "truck":
            return vehicle.capacity * EVALUATION_FACTOR;
            case "motorcycle":
            return vehicle.make * EVALUATION_FACTOR;
        }
    }
// 类型别名
    // 给类型起一个新名字
    type Message = string | string[];
    let greet = (message: Message) => {
        // ...
    };
// 交叉类型
    // 将多个类型合并成一个类型
    // 有交叉类型的所有成员
    interface IPerson {
        id: string;
        age: number;
      }
      interface IWorker {
        companyId: string;
      }
      type IStaff = IPerson & IWorker;

// 函数
    // 箭头函数
    //  参数类型和返回类型
    function createUserId(name: string, id: number): string {
        return name + id;
    }
    //可选参数及默认参数
    // 剩余参数
    // function push1(array, ...items) {
    //     items.forEach(function (item) {
    //         array.push(item);
    //       });
    // }
// 函数重载
    // 函数重载或方法重载是使用相同名称和不同参数数量或类型创建多个方法的一种能力
    // 会根据这个列表去处理函数的调用。
    type Combinable = number | string
    function add(a: number, b: number): number;
    function add(a: string, b: string): string;
    function add(a: string, b: number): string;
    function add(a: number, b: string): string;
    // 前面四个是重载的方法 下面这个只是调用
    function add(a: Combinable, b: Combinable) {
        if (typeof a === "string" || typeof b === "string") {
            return a.toString() + b.toString();
        }
        return a + b;
    }  
// TypeScript 数组
    // 数组解构
    let x: number; let y: number; let z: number;
    let five_array = [0,1,2,3,4];
    [x,y,z] = five_array;
// 数组展开运算符
    let two_array = [0, 1];
    let five_array1 = [...two_array, 2, 3, 4];
// 数组遍历
    let colors: string[] = ["red", "green", "blue"];
    for (let i of colors) {
        console.log(i);
    }
// TypeScript 对象
    let person = {
        name1: "Semlinker",
        gender: "Male",
    };
    let { name1, gender } = person;
// 对象展开运算符
    let person2 = {
        name2: "Semlinker",
        gender: "Male",
        address: "Xiamen",
    };
    // 组装对象
    let personWithAge = { ...person, age: 33 };
    // 获取除了某些项外的其它项
    let { name2, ...rest } = person2;
// TypeScript 接口
    //  对象的形状
    interface Person {
        name: string;
        age: number;
      }
    let Semlinker: Person = {
        name: "Semlinker",
        age: 33,
    };
    // 可选 | 只读属性
    interface Person1 {
        readonly name: string;
        age?: number;
    }
    // 只读属性
    // ReadonlyArray<T> 类型，它与 Array<T> 只是把所有可变方法去掉 确保数组首次创建后不可再被更改
    let a: number[] = [1, 2, 3, 4];
    let ro: ReadonlyArray<number> = a;
    // ro[0] = 12; // error!
// TypeScript 类
    // 我们可以通过 Class 关键字来定义一个类：
    // 静态属性和方法只能通过类调用 不能通过实例调用
    class Greeter {
        // 静态属性
        static cname: string = "Greeter";
        // 成员属性
        greeting: string;
      
        // 构造函数 - 执行初始化操作
        constructor(message: string) {
          this.greeting = message;
        }
        // 静态方法
        static getClassName() {
          return "Class name is Greeter";
        }
        // 成员方法
        greet() {
          return "Hello, " + this.greeting;
        }
      }
      let greeter = new Greeter("world");

    // 访问器  
    //   我们可以通过 getter 和 setter 方法来实现数据的封装和有效性校验，防止出现异常数据。
    let passcode = "Hello TypeScript";
    class Employee {
        private _fullName: string = '';
        get fullName(): string {
            return this._fullName;
        }
        set fullName(newName: string) {
            if (passcode && passcode == "Hello TypeScript") {
            this._fullName = newName;
            } else {
            console.log("Error: Unauthorized update of employee!");
            }
        }
    }
    let employee = new Employee();
    employee.fullName = "Semlinker";
    if (employee.fullName) {
        console.log(employee.fullName);
    }

    // 类的继承
    // 通过extends关键字实现继承

    // ECMAScript 私有字段
    // 私有字段以 # 字符开头，有时我们称之为私有名称
    // 私有字段不能在包含的类之外访问，甚至不能被检测到。
    // es6之后
    class Person {
        #name: string;
        constructor(name: string) {
          this.#name = name;
        }
        greet() {
            console.log(`Hello, my name is ${this.#name}!`);
        }
    }
// TypeScript 泛型
    // 泛型（Generics）是允许同一个函数接受不同类型参数的一种模板。相比于使用 any 类型，使用泛型来创建可复用的组件要更好，因为泛型会保留参数类型。
    // 泛型接口
    interface GenericIdentityFn<T> {
        (arg: T): T;
    }
    // 泛型类
    class GenericNumber<T> {
        zeroValue: T;
        add: (x: T, y: T) => T;
      }
    let myGenericNumber = new GenericNumber<number>();
    myGenericNumber.zeroValue = 0;
    myGenericNumber.add = function (x, y) {
        return x + y;
    };
// 泛型工具类型
    //   typeof  操作符可以用来获取一个变量声明或对象的类型。
    //   keyof  操作符可以用来查找一个对象中的所有 key 值：
    // in       用来遍历枚举类型：
        type Keys = "a" | "b" | "c"
        type Obj =  {
            [p in Keys]: any
        } // -> { a: any, b: any, c: any }
    // infer  在条件类型语句中，可以用 infer 声明一个类型变量并且对它进行使用。
    type ReturnType<T> = T extends (
        ...args: any[]
    ) => infer R ? R : any;
    // extends 有时候我们定义的泛型不想过于灵活或者说想继承某些类等，可以通过 extends 关键字添加泛型约束。
    interface ILengthwise {
        length: number;
      }
    function loggingIdentity<T extends ILengthwise>(arg: T): T {
        console.log(arg.length);
        return arg;
    }
    // Partial Partial<T> 的作用就是将某个类型里的属性全部变为可选项 ?。
    type Partial1<T> = {
        [P in keyof T]?: T[P];
    };
    // 在以上代码中，首先通过 keyof T 拿到 T 的所有属性名，然后使用 in 进行遍历，将值赋给 P，最后通过 T[P] 取得相应的属性值。中间的 ? 号，用于将所有属性变为可选。
      interface Todo {
        title: string;
        description: string;
      }
      function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
        return { ...todo, ...fieldsToUpdate };
      }
      
      const todo1 = {
        title: "organize desk",
        description: "clear clutter",
      };
      
      const todo2 = updateTodo(todo1, {
        description: "throw out trash",
      });
    //   我们利用 Partial<T> 工具类型，定义 fieldsToUpdate 的类型为 Partial<Todo>
    // 变成了
    // {
    //     title?: string | undefined;
    //     description?: string | undefined;
    //  }

// TypeScript 装饰器
    //   类装饰器
    // function Greeter1(greeting: string) {
    //     return function (target: Function) {
    //       target.prototype.greet = function (): void {
    //         console.log(greeting);
    //       };
    //     };
    //   }
    //   @Greeter1("Hello TS!")
    //   class Greeting {
    //     constructor() {
    //       // 内部实现
    //     }
    //   }
    //   let myGreeting = new Greeting();
    //   myGreeting.greet(); // console output: 'Hello TS!';
      

     

      
      
      




      


      
      